#include<iostream>
#include<string>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<cstdlib>
#include"Logger.hpp"

static const int gdefaultsocket = -1;

class UdpServer
{
public:
    //构造
    UdpServer(uint16_t port)//端口需要传递
    :_sockfd(gdefaultsocket)
    ,_port(port)
    ,_isrunning(false)
    {}

    void Init()
    {
        //撞见 socket fd
        _sockfd = socket(AF_INET , SOCK_DGRAM , 0);  //int socket(int domain, int type, int protocol); #include<sys/socket.h>
        //第一个参数填：AF_INET  IPv4 Internet protocols --> Udp ,第二个参数填 SOCK_DGRAM Supports datagrams 
        //第三个参数默认为0即可;返回值为文件描述符
        if(_sockfd < 0 )
        {
            LOG(LogLevel::FATAL) << "create socket error";
            exit(1);
        }
        LOG(LogLevel::INFO) << "create socket success: " << _sockfd; 

        //进行连接 --> bind
        // #include<sys/socket.h>   int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
        //返回值：成功返回0，失败返回-1
        //数据的填充 --> struct sockaddr --> struct sockaddr_in 
        struct sockaddr_in local;
        // #include<string.h> void bzero(void s[.n], size_t n);
        bzero(&local , sizeof(local));//将结构体 local 所占用的空间置空
        //填值
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);//网络规定的是大端，传到网络中需要将主机中的数据转为大端
        //在服务器中使用任意IP
        local.sin_addr.s_addr = htonl(INADDR_ANY);//inaddr_any 任意IP

        //将loacl 与 socketfd bind
        int n = bind(_sockfd , (struct sockaddr*)&local , sizeof(local));//使用的需要对第二个参数进行强制类型转换
        //判断是否bind 成功
        if(n < 0)
        {
            LOG(LogLevel::FATAL) << "bind socker error";
            exit(2);
        }
        LOG(LogLevel::INFO) << "bind socket success: " << _sockfd;
    }

    void Start()
    {
        //所有的服务器均是死循环
        _isrunning = true;
        while(_isrunning)
        {
            char buffer[1024];
            buffer[0] = 0;//清空缓冲区
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            //1、从 _sockfd 中读取数据 --> 使用 recvfrom
            //#icnlude<sys.socket.h> ssize_t recvfrom(int sockfd, void buf[restrict.len], size_t len,int flags,
            //struct sockaddr *_Nullable restrict src_addr,socklen_t *_Nullable restrict addrlen);
            //返回值：返回成功读取的字节数，读取失败返回负数
            ssize_t n = recvfrom(_sockfd , buffer ,sizeof(buffer) , 0 , (struct sockaddr*)&peer , &len);
            if(n > 0)
            {
                //client 的信息存放在 peer 中
                uint16_t clientport = ntohs(peer.sin_port);//将网络中端口转换为主机上
                std::string clientip = inet_ntoa(peer.sin_addr);
                // #include<sys.socket.h> <netinet/in.h> <arpa/inet.h> int inet_aton(const char *cp, struct in_addr *inp);
                //inet_ntoa 可以将网络中的数字ip 转换成主机上的字符串ip

                buffer[n] = 0;
                //IP + 端口
                LOG(LogLevel::DEBUG) << "[" <<clientip << ":" << clientport <<"]#" <<buffer;
                //服务端回复自己从客户端读取的数据给客户端
                std::string echo_string = "server echo#";
                echo_string += buffer;

                //sendto 服务端将信息发送给客户端
                //size_t sendto(int sockfd, const void buf[.len], size_t len, int flags,
                //const struct sockaddr *dest_addr, socklen_t addrlen);
                sendto(_sockfd , echo_string.c_str() , echo_string.size(), 0 , (struct sockaddr*)&peer , len);
            }
            _isrunning = false;
        }
    }

    void Stop()
    {
        _isrunning = false;
    }

    //析构
    ~UdpServer()
    {}
private:
    int _sockfd;//其实就是文件描述符
    //套接字 = IP + 端口
    // std::string _ip;//ip ,服务端不指定IP
    uint16_t _port;//端口

    bool _isrunning;
};